/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
 * Description: Hmfs-tools mkfs nat writer
 */

#include "nat_writer.h"

#include <cinttypes>
#include <string>
#include <sys/types.h>

#include "device_manager.h"
#include "hmfs_common.h"
#include "hmfs_io.h"
#include "hmfs_log.h"
#include "hmfs_quota.h"
#include "mkfs_format.h"
#include "securec.h"

namespace OHOS {
namespace Hmfs {
NatWriter::NatWriter(MkfsFormat &mkfs) : mkfs_(mkfs) {}

int32_t NatWriter::Format()
{
    if (InitNat() != 0) {
        return -1;
    }

    if (UpdateNatRoot() != 0) {
        return -1;
    }

    return 0;
}

int32_t NatWriter::UpdateNatRoot()
{
    HmfsData &hmfsData = mkfs_.hmfsData_;
    CmdConfig &cfgPara = mkfs_.cfgPara_;
    SuperBlockData *superBlock = mkfs_.GetSuperBlockData();
    if (superBlock == nullptr) {
        return -1;
    }

    auto natBlock = static_cast<NatBlockData *>(calloc(HMFS_BLOCK_SIZE, 1));
    if (natBlock == nullptr) {
        HMFS_ERROR("not enough memory for nat block");
        return -1;
    }

    /* quota inode */
    uint32_t id = 1;
    for (int32_t qtype = 0; qtype < MAX_QUOTAS; qtype++) {
        if (!((1 << qtype) & cfgPara.quotaBits)) {
            continue;
        }

        SetLeValue(natBlock->entries[GetLeValue(superBlock->quotaInodeId[qtype])].blockId,
            hmfsData.mainStartBlkId + hmfsData.currentSegments[CURSEG_NODE_HOT] * BLOCKS_PER_SEGMENT + id++);
        natBlock->entries[GetLeValue(superBlock->quotaInodeId[qtype])].inodeNumber = superBlock->quotaInodeId[qtype];
    }

    /* root inode */
    SetLeValue(natBlock->entries[hmfsData.rootInode].blockId,
        hmfsData.mainStartBlkId + hmfsData.currentSegments[CURSEG_NODE_HOT] * BLOCKS_PER_SEGMENT);
    SetLeValue(natBlock->entries[hmfsData.rootInode].inodeNumber, hmfsData.rootInode);

    /* update node nat */
    SetLeValue(natBlock->entries[hmfsData.nodeInode].blockId, 1);
    SetLeValue(natBlock->entries[hmfsData.nodeInode].inodeNumber, hmfsData.nodeInode);

    /* update meta nat */
    SetLeValue(natBlock->entries[hmfsData.metaInode].blockId, 1);
    SetLeValue(natBlock->entries[hmfsData.metaInode].inodeNumber, hmfsData.metaInode);

    uint64_t bolckId = hmfsData.natStartBlkId;
    HMFS_DEBUG("Writing nat root at block 0x%08" PRIx64 "", bolckId);
    if (HmfsIo::GetInstance().DevWriteBlock(natBlock, bolckId)) {
        HMFS_ERROR("failed to write nat block 0 to disk");
        free(natBlock);
        return -1;
    }
    free(natBlock);
    return 0;
}

int32_t NatWriter::InitNat()
{
    HmfsData &hmfsData = mkfs_.hmfsData_;
    uint32_t segmentSize = BLOCKS_PER_SEGMENT * HMFS_BLOCK_SIZE;
    auto buf = static_cast<uint8_t *>(calloc(sizeof(uint8_t), segmentSize));
    if (buf == nullptr) {
        HMFS_ERROR("not enough memory for nat segment!");
        return -1;
    }
    uint64_t offset = hmfsData.natStartBlkId * HMFS_BLOCK_SIZE;
    uint32_t segmentWriteCount = hmfsData.segmentCountInNAT / HMFS_NAT_COUNT;
    HMFS_DEBUG("Formating nat segment at offset 0x%08" PRIx64 "", offset);
    for (uint32_t i = 0; i < segmentWriteCount; i++) {
        if (HmfsIo::GetInstance().DevFill(buf, offset, segmentSize)) {
            HMFS_ERROR("failed to write nat segment");
            free(buf);
            return -1;
        }
        offset += (segmentSize * HMFS_NAT_COUNT);
    }
    free(buf);

    return 0;
}

} // namespace Hmfs
} // namespace OHOS
